use std::ops;

#[derive(Debug,Clone,PartialEq)]
pub enum Parser{
    CONS(Box<Parser>,Box<Parser>),
    CHAR(char),
    OR(Box<Parser>,Box<Parser>),
    MAYBE(Box<Parser>),//0-1
    ANY(Box<Parser>),//0-n
    MORE(Box<Parser>),//1-n
}

#[derive(Debug,Clone,PartialEq)]
pub enum ResultType{
    CONS(Box<Result>,Box<Result>),
    CHAR,
    OR(Box<Result>),
    MAYBE(Box<Result>),//0-1
    ANY(Box<Vec<Result>>),//0-n
    MORE(Box<Vec<Result>>),//1-n
    EMPTY,
}

#[derive(Debug,Clone,PartialEq)]
pub struct Result {
    pub t:ResultType,
    pub pos: usize,
    pub length: usize,
}



impl ops::Add<Parser> for Parser {
    type Output = Parser;

    fn add(self, _rhs: Parser) ->  Parser  {
        Parser::CONS(Box::new(self),Box::new(_rhs))
    }
}
impl ops::BitOr<Parser> for Parser {
    type Output = Parser;

    fn bitor(self, _rhs: Parser) -> Parser {
        Parser::OR(Box::new(self),Box::new(_rhs))
    }
}

impl Parser {
    pub fn more(&self)->Parser{
        Parser::MORE(Box::new(self.clone()))
    }
    pub fn any(&self)->Parser{
        Parser::ANY(Box::new(self.clone()))
    }
    pub fn maybe(&self)->Parser{
        Parser::MAYBE(Box::new(self.clone()))
    }
    pub fn parse(&self,ss: &String,pos:usize)->Option<Result>{
        let s=ss.as_bytes();
        if pos >= s.len(){
            return None
        }
        match self {
            Parser::CHAR(a) => {
                if pos<s.len() && s[pos] as char == *a  {
                    return Some(Result{t:ResultType::CHAR,pos:pos,length:1})
                }else{
                    return None
                }
            }
            Parser::CONS( a , b) => {
                match (*a).parse(ss,pos) {
                    Some(t) => {
                        match b.parse(ss,pos+t.length) {
                            Some(y) => {
                                let q= t.pos;
                                return Some(Result{t:ResultType::CONS(Box::new(t.clone()),Box::new(y.clone())),pos:q,length:(t.length+y.length)})
                            }
                            x=>{return x;}
                        }
                    }
                    n=>{
                        return n
                    }
                }

            }
            Parser::OR(a,b) => {
                match (*a).parse(ss,pos) {
                    Some(t) => {
                        let q = t.pos;
                        return Some(Result{t:ResultType::OR(Box::new(t.clone())),pos:q,length:(t.length )})
                    }
                    _n=>{
                        match b.parse(ss,pos) {
                            Some(y) => {
                                let q = y.pos;
                                return Some(Result{t:ResultType::OR(Box::new(y.clone())),pos:q,length:(y.length )})
                            }
                            x=>{return x;}
                        }
                    }
                }
            }
            Parser::ANY(a) => {
                match (*a).parse(ss,pos) {
                    Some(t) => {
                        let mut l =t.length;
                        //let mut t1=Result{t:ResultType::ANY(Box::new(t.clone()),Box::new(Result{t:ResultType::EMPTY,pos:q,length:0})),pos:q,length:l};
                        let mut t1:Vec<Result>=vec![];
                        t1.push(t.clone());
                        'outer: loop {
                            match (*a).clone().parse(ss,pos+l) {
                                Some(y) => {
                                    l = l + y.length ;
                                    t1.push(y.clone());
                                    //t1=Result{t:ResultType::ANY(Box::new(y.clone()),Box::new(t1.clone())),pos:q,length:l}
                                }
                                _x=>{break 'outer;}
                            }
                        }
                        return Some(Result{t:ResultType::ANY(Box::new(t1)),pos:pos,length:l})

                    }
                    _n=>{
                        let  t1:Vec<Result>=vec![];
                        //return Some(Result{t:ResultType::ANY(Box::new(Result{t:ResultType::EMPTY,pos:pos,length:0}),Box::new(Result{t:ResultType::EMPTY,pos:pos,length:0})),pos:pos,length:0})
                        return Some(Result{t:ResultType::ANY(Box::new(t1)),pos:pos,length:0})
                    }
                }
            }
            Parser::MAYBE(a) => {
                match (*a).parse(ss,pos) {
                    Some(t) => {
                        let q=t.pos;
                        return Some(Result{t:ResultType::MAYBE(Box::new(t.clone())),pos:q,length:(t.length )})
                    }
                    _n=>{
                        return Some(Result{t:ResultType::MAYBE(Box::new(Result{t:ResultType::EMPTY,pos:pos,length:0})),pos:pos,length:0})
                    }
                }
            }
            Parser::MORE(a) => {
                match (*a).parse(ss,pos) {
                    Some(t) => {
                        let mut l = t.length;
                        let mut t1:Vec<Result>=vec![];
                        t1.push(t.clone());
                        //let mut t1=Result{t:ResultType::MORE(Box::new(t.clone()),Box::new(Result{t:ResultType::EMPTY,pos:pos,length:0})),pos:pos,length:l};
                        'outer1: loop {
                            match (*a).parse(ss,pos+l) {
                                Some(y) => {
                                    //let p = t1.pos;
                                    //t1=Result{t:ResultType::MORE(Box::new(y.clone()),Box::new(t1.clone())),pos:p,length:(t1.length+y.length)}
                                    l = l + y.length ;
                                    t1.push(y.clone());
                                }
                                _x=>{break 'outer1;}
                            }
                        }
                        return Some(Result{t:ResultType::MORE(Box::new(t1)),pos:pos,length:l})
                        //return Some(t1)

                    }
                    n=>{
                        return n
                    }
                }
            }
        }
    }
}


impl Result {

}